// server_length.go
//
// 功能：采用“长度前缀 + payload”协议解决粘包问题。
// 协议格式： [4 字节大端长度][Payload]
// 步骤：先读定长头，再按长度读取消息体，确保一次解析一条完整消息。
// 业务：打印消息内容并按相同协议回显。
package main

import (
	"encoding/binary"
	"fmt"
	"io"
	"log"
	"net"
)

const lengthAddr = "127.0.0.1:7100"

func main() {
	listener, err := net.Listen("tcp", lengthAddr)
	if err != nil {
		log.Fatalf("listen error: %v", err)
	}
	log.Printf("[LEN] 监听 %s，等待客户端连接...", lengthAddr)

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Printf("[LEN] accept error: %v", err)
			continue
		}
		go handleConn(conn)
	}
}

// handleConn 读取长度前缀帧并处理业务。
func handleConn(conn net.Conn) {
	defer func() {
		log.Printf("[LEN] 关闭连接 remote=%s", conn.RemoteAddr())
		conn.Close()
	}()

	for {
		lengthBuf := make([]byte, 4)
		// io.ReadFull 保证读取到指定字节，否则返回错误
		if _, err := io.ReadFull(conn, lengthBuf); err != nil {
			if err == io.EOF {
				log.Println("[LEN] 客户端正常断开")
			} else {
				log.Printf("[LEN] 读取长度头失败: %v", err)
			}
			return
		}
		payloadLen := binary.BigEndian.Uint32(lengthBuf)
		payload := make([]byte, payloadLen)

		if _, err := io.ReadFull(conn, payload); err != nil {
			log.Printf("[LEN] 读取消息体失败: %v", err)
			return
		}

		log.Printf("[LEN] 收到完整消息 len=%d 内容=%q", payloadLen, payload)

		// 回显：同样使用长度前缀打包
		resp := []byte(fmt.Sprintf("ack:%s", payload))
		respBuf := make([]byte, 4+len(resp))
		binary.BigEndian.PutUint32(respBuf[:4], uint32(len(resp)))
		copy(respBuf[4:], resp)

		if _, err := conn.Write(respBuf); err != nil {
			log.Printf("[LEN] 发送回显失败: %v", err)
			return
		}
	}
}
